import random
import math
import time
from enum import Enum


class UnitType(Enum):
    HERO = "英雄"
    SOLDIER = "小兵"
    MONSTER = "野怪"
    WORKER = "农民"
    BUILDING = "建筑"


class Attribute(Enum):
    STRENGTH = "力量"
    INTELLIGENCE = "智力"
    STAMINA = "耐力"


class ResourceType(Enum):
    GOLD = "金币"
    WOOD = "木材"


class GameState:
    def __init__(self):
        self.units = []
        self.buildings = []
        self.resources = {ResourceType.GOLD: 1000, ResourceType.WOOD: 500}
        self.game_time = 0
        self.tech_tree = {
            "Town Hall": ["Barracks", "Farm"],
            "Barracks": ["Blacksmith"],
            "Farm": [],
            "Blacksmith": []
        }

    def update(self, dt):
        self.game_time += dt
        new_units = []
        for unit in self.units:
            if hasattr(unit, 'expire_time') and self.game_time > unit.expire_time:
                print(f"{unit.name}消失了!")
            else:
                unit.update(dt, self)  # 更新单位状态
                new_units.append(unit)
        self.units = new_units
        for building in self.buildings:
            building.update(dt, self)  # 更新建筑状态

    def add_unit(self, unit):
        self.units.append(unit)

    def add_building(self, building):
        self.buildings.append(building)
        building.is_built = True  # 建筑添加即视为已建造

    def spend_resources(self, cost):
        for resource, amount in cost.items():
            if self.resources[resource] < amount:
                return False
        for resource, amount in cost.items():
            self.resources[resource] -= amount
        return True

    def has_building(self, building_name):
        for building in self.buildings:
            if building.name == building_name:
                return True
        return False


class Position:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def distance_to(self, other):
        if isinstance(other, Position):
            return math.hypot(self.x - other.x, self.y - other.y)
        elif isinstance(other, tuple) and len(other) == 2:
            return math.hypot(self.x - other[0], self.y - other[1])
        return 0

    def move_towards(self, target, speed, dt):
        if isinstance(target, Position):
            target_x, target_y = target.x, target.y
        elif isinstance(target, tuple) and len(target) == 2:
            target_x, target_y = target
        else:
            return

        dx = target_x - self.x
        dy = target_y - self.y
        distance = math.hypot(dx, dy)
        if distance > 0:
            dx, dy = dx / distance, dy / distance
            move_distance = min(distance, speed * dt)
            self.x += dx * move_distance
            self.y += dy * move_distance

    def __str__(self):
        return f"({self.x:.1f},{self.y:.1f})"


class Unit:
    def __init__(self, name, unit_type, position, max_health, attack_damage, attack_range, attack_speed,
                 movement_speed):
        self.name = name
        self.unit_type = unit_type
        self.position = position
        self.max_health = max_health
        self.health = max_health
        self.attack_damage = attack_damage
        self.attack_range = attack_range
        self.attack_cooldown = 0
        self.attack_speed = attack_speed
        self.movement_speed = movement_speed
        self.target = None
        self.team = "Player"
        self.is_moving = False
        self.move_target = None
        self.is_building = False
        self.building_target = None
        self.build_progress = 0
        self.current_time = 0  # 存储当前游戏时间

    def is_alive(self):
        return self.health > 0

    def take_damage(self, damage):
        self.health = max(0, self.health - damage)
        return not self.is_alive()

    def attack(self, target):
        if self.attack_cooldown <= 0 and self.position.distance_to(target.position) <= self.attack_range:
            killed = target.take_damage(self.attack_damage)
            self.attack_cooldown = 1.0 / self.attack_speed
            return killed
        return False

    def move_to(self, position):
        self.is_moving = True
        self.move_target = position
        self.target = None

    def update(self, dt, game_state):
        self.current_time = game_state.game_time
        if self.attack_cooldown > 0:
            self.attack_cooldown = max(0, self.attack_cooldown - dt)  # 冷却缩减

        # 移动到目标位置
        if self.is_moving and self.move_target:
            self.position.move_towards(self.move_target, self.movement_speed, dt)
            if self.position.distance_to(self.move_target) < 0.1:
                self.is_moving = False
                self.move_target = None

        # 清理死亡目标
        if self.target and not self.target.is_alive():
            self.target = None

        # 自动寻找攻击目标（攻击范围内）
        if not self.target and not self.is_moving:
            for unit in game_state.units:
                if unit.team != self.team and unit.is_alive():
                    if self.position.distance_to(unit.position) <= self.attack_range:
                        self.target = unit
                        break

        if self.target and self.target.is_alive() and not self.is_moving:
            if self.position.distance_to(self.target.position) <= self.attack_range:
                killed = self.attack(self.target)  # 攻击目标
                if killed:
                    print(f"{self.name}击杀了{self.target.name}!")
            else:
                self.move_to(self.target.position)  # 设置移动目标为攻击目标位置

    def distance_to(self, x, y):
        return math.hypot(self.position.x - x, self.position.y - y)

    def __str__(self):
        action = ""
        if self.is_building and self.building_target:
            progress = min(100, int(self.build_progress / self.building_target.build_time * 100))
            action = f" [建造{self.building_target.name}:{progress}%]"
        duration_info = ""
        if hasattr(self, 'expire_time'):
            remaining = max(0, self.expire_time - self.current_time)
            duration_info = f" [剩余:{remaining:.1f}s]"
        return f"{self.name}{action}{duration_info} HP:{self.health}/{self.max_health} ATK:{self.attack_damage} POS:{self.position}"


class Hero(Unit):
    def __init__(self, name, position, game_state_ref=None):
        super().__init__(name, UnitType.HERO, position, 500, 30, 1.5, 0.7, 2.8)
        self.level = 1
        self.experience = 0
        self.attributes = {Attribute.STRENGTH: 15, Attribute.INTELLIGENCE: 28, Attribute.STAMINA: 18}
        self.skills = []
        self.mana = 150
        self.max_mana = 150
        self.game_state_ref = game_state_ref  # 游戏状态引用（用于技能）

    def level_up(self):
        self.level += 1
        self.attributes[Attribute.STRENGTH] += 1
        self.attributes[Attribute.INTELLIGENCE] += 3
        self.attributes[Attribute.STAMINA] += 1
        self.max_health = 500 + self.attributes[Attribute.STAMINA] * 10
        self.attack_damage = 30 + self.attributes[Attribute.INTELLIGENCE] * 0.8
        self.max_mana = 150 + self.attributes[Attribute.INTELLIGENCE] * 5
        self.health = self.max_health
        self.mana = self.max_mana
        return f"{self.name}升级到{self.level}级! 智力:{self.attributes[Attribute.INTELLIGENCE]}"

    def gain_experience(self, amount):
        self.experience += amount
        required_exp = self.level * 100
        if self.experience >= required_exp:
            self.experience -= required_exp
            return self.level_up()
        return None

    def learn_skill(self, skill):
        if skill not in self.skills:
            self.skills.append(skill)

    def use_skill(self, skill, target=None):
        if skill in self.skills and self.mana >= skill.mana_cost:
            self.mana -= skill.mana_cost
            return skill.use(self, target)
        return False

    def update(self, dt, game_state):
        super().update(dt, game_state)
        # 更新技能冷却
        for skill in self.skills:
            if skill.current_cooldown > 0:
                skill.current_cooldown = max(0, skill.current_cooldown - dt)
        # 更新魔法恢复
        base_mana_recovery = self.attributes[Attribute.INTELLIGENCE] * 0.07 * dt
        self.mana = min(self.max_mana, self.mana + base_mana_recovery)


class Worker(Unit):
    def __init__(self, name, position):
        super().__init__(name, UnitType.WORKER, position, 200, 5, 1.0, 0.5, 2.5)
        self.team = "Player"
        self.is_building = False
        self.building_target = None
        self.build_progress = 0

    def build(self, building_type, position, game_state):
        building_instance = building_type(position.x, position.y)
        cost = building_instance.cost
        if game_state.spend_resources(cost):  # 检查资源
            self.is_building = True
            self.building_target = building_instance
            self.build_progress = 0
            return True
        return False

    def update(self, dt, game_state):
        super().update(dt, game_state)
        # 更新建造进度
        if self.is_building and self.building_target:
            self.build_progress += dt
            if self.build_progress >= self.building_target.build_time:
                building_name = self.building_target.name
                game_state.add_building(self.building_target)
                self.is_building = False
                self.building_target = None
                return f"{self.name}完成了{building_name}建造!"
        return None


class Soldier(Unit):
    def __init__(self, name, position, unit_type=UnitType.SOLDIER):
        super().__init__(name, unit_type, position, 250, 15, 1.2, 1.2, 2.8)
        self.team = "Player"


class Footman(Soldier):
    cost = {ResourceType.GOLD: 135, ResourceType.WOOD: 0}
    production_time = 20

    def __init__(self, position):
        super().__init__("步兵", position)


class Rifleman(Soldier):
    cost = {ResourceType.GOLD: 150, ResourceType.WOOD: 30}
    production_time = 30

    def __init__(self, position):
        super().__init__("火枪手", position)
        self.max_health = 180
        self.health = 180
        self.attack_damage = 25
        self.attack_range = 3.0
        self.attack_speed = 1.5
        self.movement_speed = 2.5


class Monster(Unit):
    def __init__(self, name, position):
        super().__init__(name, UnitType.MONSTER, position, 300, 20, 1.3, 0.8, 2.2)
        self.team = "Enemy"


class Building:
    def __init__(self, name, position, max_health, build_time):
        self.name = name
        self.position = position
        self.max_health = max_health
        self.health = max_health
        self.build_time = build_time
        self.is_built = False
        self.production_queue = []
        self.production_progress = 0

    def is_alive(self):
        return self.health > 0

    def take_damage(self, damage):
        self.health = max(0, self.health - damage)
        return not self.is_alive()

    def produce_unit(self, unit_type, game_state):
        cost = unit_type.cost
        if game_state.spend_resources(cost):
            self.production_queue.append(unit_type)
            return True
        return False

    def update(self, dt, game_state):
        if not self.is_built:  # 未完成建筑不生产
            return None
        # 更新生产进度
        if self.production_queue:
            unit_type = self.production_queue[0]
            self.production_progress += dt
            if self.production_progress >= unit_type.production_time:
                x = self.position.x + random.uniform(-2, 2)
                y = self.position.y + random.uniform(-2, 2)
                unit = unit_type(Position(x, y))
                game_state.add_unit(unit)  # 生产完成
                self.production_progress = 0
                self.production_queue.pop(0)
                return f"{self.name}生产了{unit.name}"
        return None

    def distance_to(self, x, y):
        return math.hypot(self.position.x - x, self.position.y - y)

    def __str__(self):
        action = ""
        if self.production_queue:
            progress = min(100, int(self.production_progress / self.production_queue[0].production_time * 100))
            action = f" [生产{self.production_queue[0].__name__}:{progress}%]"
        return f"{self.name} HP:{self.health}/{self.max_health} POS:{self.position}{action}"


class TownHall(Building):
    cost = {ResourceType.GOLD: 400, ResourceType.WOOD: 200}

    def __init__(self, x, y):
        super().__init__("城镇大厅", Position(x, y), 1200, 30)


class Barracks(Building):
    cost = {ResourceType.GOLD: 200, ResourceType.WOOD: 150}

    def __init__(self, x, y):
        super().__init__("兵营", Position(x, y), 800, 25)
        self.available_units = [Footman]


class Blacksmith(Building):
    cost = {ResourceType.GOLD: 175, ResourceType.WOOD: 100}

    def __init__(self, x, y):
        super().__init__("铁匠铺", Position(x, y), 700, 20)


class Skill:
    def __init__(self, name, mana_cost, cooldown):
        self.name = name
        self.mana_cost = mana_cost
        self.cooldown = cooldown
        self.current_cooldown = 0

    def use(self, caster, target=None):
        if self.current_cooldown <= 0:
            self.current_cooldown = self.cooldown
            return self.apply_effect(caster, target)
        return f"{self.name}技能冷却中，剩余{self.current_cooldown:.1f}秒"

    def apply_effect(self, caster, target):
        pass


class SummonWaterElemental(Skill):
    def __init__(self):
        super().__init__("召唤水元素", 100, 15)

    def apply_effect(self, caster, target=None):
        if not hasattr(caster, 'game_state_ref') or caster.game_state_ref is None:
            return "无法召唤水元素：缺少游戏状态引用"
        angle = random.uniform(0, 2 * math.pi)
        distance = 2.0
        x = caster.position.x + math.cos(angle) * distance
        y = caster.position.y + math.sin(angle) * distance
        water_elemental = Unit("水元素", UnitType.SOLDIER, Position(x, y), 300, 25, 1.5, 1.0, 2.0)
        water_elemental.team = "Player"
        water_elemental.duration = 30.0
        water_elemental.expire_time = caster.game_state_ref.game_time + water_elemental.duration
        caster.game_state_ref.add_unit(water_elemental)  # 添加召唤单位
        return f"{caster.name}召唤了一个水元素！持续30秒"


class BrillianceAura(Skill):
    def __init__(self):
        super().__init__("辉煌光环", 0, 0)

    def apply_effect(self, caster, target=None):
        return f"{caster.name}的辉煌光环提高了魔法恢复速度！"


class GameCLI:
    def __init__(self):
        self.game_state = GameState()
        self.player_hero = None
        self.initialize_game()
        self.last_result = ""

    def initialize_game(self):
        worker = Worker("农民1", Position(5, 5))
        self.game_state.add_unit(worker)
        hero = Hero("大法师", Position(6, 6), self.game_state)
        hero.learn_skill(SummonWaterElemental())
        hero.learn_skill(BrillianceAura())
        self.game_state.add_unit(hero)
        self.player_hero = hero
        monster1 = Monster("森林巨魔", Position(15, 15))
        monster2 = Monster("食人魔", Position(18, 18))
        self.game_state.add_unit(monster1)
        self.game_state.add_unit(monster2)
        town_hall = TownHall(5, 7)
        town_hall.is_built = True
        self.game_state.add_building(town_hall)

    def print_game_state(self):
        print(
            f"时间: {self.game_state.game_time:.1f}s 金:{self.game_state.resources[ResourceType.GOLD]} 木:{self.game_state.resources[ResourceType.WOOD]}")
        if self.player_hero:
            print(
                f"英雄: {self.player_hero.name} Lv.{self.player_hero.level} HP:{self.player_hero.health}/{self.player_hero.max_health} MP:{self.player_hero.mana:.0f}/{self.player_hero.max_mana} 位置:{self.player_hero.position}")
        print("单位:")
        for unit in self.game_state.units:
            if unit != self.player_hero:
                print(f"  {unit}")
        print("建筑:")
        for building in self.game_state.buildings:
            print(f"  {building}")

    def process_command(self, command):
        parts = command.split()
        if not parts:
            return True, self.last_result
        cmd = parts[0].lower()
        result = ""

        # 命令处理逻辑
        if cmd == "move" and len(parts) == 3 and self.player_hero:
            try:
                x, y = float(parts[1]), float(parts[2])
                self.player_hero.move_to((x, y))
                result = f"英雄移动到({x},{y})"
            except ValueError:
                result = "无效的坐标"

        elif cmd == "attack" and len(parts) >= 2 and self.player_hero:
            target_name = " ".join(parts[1:])
            for unit in self.game_state.units:
                if unit.name.lower() == target_name.lower() and unit.team == "Enemy":
                    self.player_hero.target = unit
                    result = f"英雄开始攻击{unit.name}"
                    break
            else:
                result = "未找到目标"

        elif cmd == "build" and len(parts) >= 2:
            building_type = " ".join(parts[1:])
            worker = self.find_available_worker()
            if worker:
                if building_type == "兵营":
                    if worker.build(Barracks, Position(worker.position.x + 3, worker.position.y), self.game_state):
                        result = f"{worker.name}开始建造兵营"
                    else:
                        result = "资源不足"
                elif building_type == "铁匠铺":
                    if worker.build(Blacksmith, Position(worker.position.x + 3, worker.position.y), self.game_state):
                        result = f"{worker.name}开始建造铁匠铺"
                    else:
                        result = "资源不足"
                else:
                    result = "未知建筑类型"
            else:
                result = "没有可用的农民"

        elif cmd == "cast" and len(parts) >= 2 and self.player_hero:
            skill_name = " ".join(parts[1:])
            for skill in self.player_hero.skills:
                if skill.name == skill_name:
                    skill_result = self.player_hero.use_skill(skill)
                    if skill_result:
                        result = skill_result
                        break
            else:
                result = f"未知技能:{skill_name}"

        elif cmd == "produce" and len(parts) >= 2:
            unit_type = " ".join(parts[1:])
            for building in self.game_state.buildings:
                if building.name == "兵营":
                    if unit_type == "步兵":
                        if building.produce_unit(Footman, self.game_state):
                            result = "开始生产步兵"
                    elif unit_type == "火枪手":
                        if self.game_state.has_building("铁匠铺"):
                            if building.produce_unit(Rifleman, self.game_state):
                                result = "开始生产火枪手"
                        else:
                            result = "需要先建造铁匠铺"
            if not result:
                result = "无法生产该单位"

        elif cmd == "help":
            self.show_commands()
            return True, "显示帮助"

        elif cmd == "exit":
            return False, "退出游戏"

        return True, result or f"未知命令 (输入 'help' 查看帮助)"

    def show_commands(self):
        print("\n命令列表:")
        print("  move x y         移动英雄到指定位置")
        print("  attack 目标       攻击指定单位")
        print("  build 建筑        建造建筑(兵营,铁匠铺)")
        print("  cast 技能         释放技能（召唤水元素）")
        print("  produce 单位      生产单位(步兵,火枪手)")
        print("  help             显示帮助")
        print("  exit             退出游戏")

    def find_available_worker(self):
        for unit in self.game_state.units:
            if isinstance(unit, Worker) and not unit.is_building:
                return unit
        return None

    def run(self):
        print("魔兽争霸风格游戏 - 输入help查看命令")
        running = True
        last_time = time.time()

        while running:
            current_time = time.time()
            dt = current_time - last_time
            last_time = current_time
            self.game_state.update(dt)  # 更新游戏状态
            self.print_game_state()
            print("命令: build 建筑 | produce 单位 | move x y | attack 目标 | cast 技能 | help | exit")
            if self.last_result:
                print(f"{self.last_result}")
            command = input("> ")
            running, self.last_result = self.process_command(command)


if __name__ == "__main__":
    game = GameCLI()
    game.run()
